﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Office.Spreadsheet.Expressions;
using Office.Spreadsheet.Parsing;

namespace Office.Spreadsheet.UI
{
    internal class RangeFunctions
    {
        public static RangeFunction ApplyAll
        {
            get
            {
                return new RangeTextFunction("All",
                                             cell =>
                                                 {
                                                     cell.SetStyleRecordSafe(null);
                                                     cell.Formula = null;
                                                     cell.Value = null;
                                                     cell.Comment = null;
                                                 },
                                             dim => dim.ClearStyleRecord(),
                                             CopyValueStyleAndComment,
                                             (source, destination) => source.SetStyleRecordSafe(destination.StyleRecord),
                                             CopyRowColumnAll, TableFunctions.ClearAndCopyTablesAndConditionalFormatting, CopyDataValidation);
            }
        }
        public static RangeFunction ApplyAllAndFormula
        {
            get
            {
                return new RangeFunction(
                    cell =>
                        {
                            cell.SetStyleRecordSafe(null);
                            cell.Formula = null;
                            cell.Value = null;
                            cell.Comment = null;
                        },
                    dim => dim.ClearStyleRecord(),
                    CopyValueAndFormulaAndStyleRecord,
                    (source, destination) => source.SetStyleRecordSafe(destination.StyleRecord),
                    CopyRowColumnAll,
                    TableFunctions.ClearAndCopyTablesAndConditionalFormatting
                    ,
                    CopyDataValidation)
                    ;
            }
        }

       
        private static void CopyDataValidation(Range source, Range destination, PasteInfo pasteInfo)
        {
            if (pasteInfo.Transpose)
            {
                Debug.WriteLine("Transpose not implemented for data validation");
                return;
            }
            CellRelativeAddress offset = destination.TopLeftCell.Address - source.Worksheet.Selection.TopLeftCell.Address;
            List<DataValidation> dataValidations =
                Enumerable.ToList(Enumerable.Where(source.Worksheet.DataValidations,
                                                   dv => Range.Intersects(source, dv.Range)));
            foreach (var dataValidation in dataValidations)
            {
                var newDataValidation = dataValidation.Projected(destination.Worksheet, offset);
                destination.Worksheet.DataValidations.Add(newDataValidation);
            }
        }


        public static Action<Dimension> ClearDimensionStyleRecord
        {
            get { return delegate(Dimension dim) { dim.ClearStyleRecord(); }; }
        }

        public static RangeFunction ApplyValue
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell) { cell.Value = null; },
                    null,
                    CopyValue,
                    null, null, TableFunctions.None, CopyNoRange);
            }
        }

        private static void CopyNoRange(Range source, Range destination, PasteInfo pasteInfo)
        {
            
        }

        public static RangeFunction ApplyValueAndFormula
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell)
                        {
                            cell.Value = null;
                            cell.Formula = null;
                        },
                    null,
                    CopyValueAndFormula,
                    //   delegate(Cell source, Cell dest, PasteInfo pasteInfo)
                    //        {
                    //          dest.Value = source.Value;
                    //            dest.Formula = source.Formula;
                    //      },
                    null, null, TableFunctions.None, CopyNoRange);
            }
        }

        private static TernaryAction<Cell, Cell, PasteInfo> CopyValueStyleAndComment
        {
            get
            {
                return delegate(Cell source, Cell dest, PasteInfo pasteInfo)
                           {
                               dest.Value = source.Value;
                               dest.SetStyleRecordSafe(source.StyleRecord);
                               dest.Comment = source.Comment;
                               //\\missing validation
                           };
            }
        }

        private static BinaryAction<Dimension, Dimension> CopyRowColumnAll
        {
            get
            {
                return delegate(Dimension source, Dimension destination)
                           {
                               if (source is Row)
                               {
                                   Row rSource = (Row) source;
                                   Row rDestination = (Row) destination;
                                   rDestination.Height = rSource.Height;
                               }
                               else
                               {
                                   Column rSource = (Column) source;
                                   Column rDestination = (Column) destination;
#if DEBUG
                                   Debug.WriteLine("rSource = " + rSource.Label);
                                   Debug.WriteLine("rDestination = " + rDestination.Label);
                                   var w = rSource.Width;
                                   Debug.WriteLine("rSource.Width = " + w);
#endif
                                   rDestination.Width = rSource.Width;
                               }
                           };
            }
        }

        private static BinaryAction<Dimension, Dimension> CopyRowColumnNone
        {
            get { return delegate { }; }
        }

        public static RangeFunction ApplyAllExceptBorders
        {
            get
            {
                return new RangeFunction(
                    (cell) =>
                        {
                            Borders b = cell.Borders;
                            cell.SetStyleRecordSafe(null);
                            cell.Borders = b;
                        },
                    delegate
                        {
                            //\\todo?
                        },
                    CopyAllExceptBorders,
                    null, CopyRowColumnAll, TableFunctions.None, CopyDataValidation
                    );
            }
        }

        private static TernaryAction<Cell, Cell, PasteInfo> CopyAllExceptBorders
        {
            get
            {
                return delegate(Cell source, Cell dest, PasteInfo pasteInfo)
                           {
                               Borders b = dest.Borders;
                               CopyValueStyleAndComment(source, dest, pasteInfo);
                               dest.Borders = b;
                           };
            }
        }

        public static RangeFunction ApplyColumnWidth
        {
            get
            {
                return new RangeFunction(
                    delegate { },
                    dim =>
                        {
                            if (dim is Column)
                                ((Column) dim).Width = null;
                        },
                    CopyColumnWidth,
                    (source, destination) =>
                        {
                            if (destination is Column)
                            {
                                ((Column) destination).Width = ((Column) source).Width; //\\
                            }
                        }, CopyRowColumnAll, TableFunctions.None, CopyNoRange
                    );
            }
        }

        private static TernaryAction<Cell, Cell, PasteInfo> CopyColumnWidth
        {
            get { return (source, dest, pasteInfo) => dest.Column.Width = source.Column.Width; }
        }

        public static RangeFunction ApplyFormats
        {
            get
            {
                return new RangeFunction(
                    cell => cell.SetStyleRecordUnsafe(null),
                    dim => dim.ClearStyleRecord(),
                    (source, destination, pasteInfo) => destination.SetStyleRecordSafe(source.StyleRecord),
                    (source, destination) => destination.SetStyleRecordSafe(source.StyleRecord), CopyRowColumnAll
                    , TableFunctions.None, CopyDataValidation
                    );
            }
        }

        public static RangeFunction ApplyFormulaAndFormat
        {
            get
            {
                return new RangeFunction(
                    cell =>
                        {
                            cell.Formula = null;
                            cell.Formula = null;
                        },
                    null,
                    (source, destination, pasteInfo) =>
                        {
                            destination.Formula = source.Formula;
                            destination.Format = source.Format;
                        },
                    delegate(Dimension source, Dimension destination) { destination.Format = source.Format; }
                    , CopyRowColumnAll, TableFunctions.None, CopyDataValidation); //\\ sure?
            }
        }

        public static RangeFunction ApplyComment
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell) { cell.Comment = null; }, null,
                    delegate(Cell source, Cell destination, PasteInfo pasteInfo) { destination.Comment = source.Comment; },
                    null, null, TableFunctions.None, CopyNoRange);
            }
        }

        public static RangeFunction ApplyValueAndFormat
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell)
                        {
                            cell.Value = null;
                            cell.Format = null;
                        },
                    delegate(Dimension dim) { dim.Format = null; },
                    delegate(Cell source, Cell destination, PasteInfo pasteInfo)
                        {
                            destination.Value = source.Value;
                            destination.Format = source.Format;
                        },
                    delegate { }, //\\todo
                    null, TableFunctions.None, CopyDataValidation); //\\ sure?
            }
        }

        public static RangeFunction ApplyvalueAndStyleRecord
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell)
                        {
                            cell.Value = null;
                            cell.SetStyleRecordSafe(null);
                        },
                    delegate(Dimension dim) { dim.ClearStyleRecord(); },
                    delegate(Cell source, Cell destination, PasteInfo pasteInfo)
                        {
                            destination.Value = source.Value;
                            destination.SetStyleRecordSafe(source.StyleRecord);
                        },
                    delegate(Dimension source, Dimension destination) { destination.SetStyleRecordSafe(source.StyleRecord); }
                    , CopyRowColumnAll, TableFunctions.None, CopyNoRange);
            }
        }

        public static RangeFunction ApplyValueAndFormulaAndStyleRecord
        {
            get
            {
                return new RangeFunction(
                    cell =>
                        {
                            cell.Value = null;
                            cell.Format = null;
                            cell.SetStyleRecordSafe(null);
                        },
                    dim =>
                        {
                            dim.ClearStyleRecord();
                            dim.Format = null;
                        },
                    CopyValueAndFormulaAndStyleRecord,
                    (source, destination) => destination.SetStyleRecordSafe(source.StyleRecord), CopyRowColumnNone
                    , TableFunctions.ClearAndCopyTablesAndConditionalFormatting, CopyDataValidation
                    );
            }
        }

        private static TernaryAction<Cell, Cell, PasteInfo> CopyValueAndFormulaAndStyleRecord
        {
            get
            {
                return delegate(Cell source, Cell destination, PasteInfo info)
                           {
                               destination.SetStyleRecordSafe(source.StyleRecord);
                               CopyValueAndFormula(source, destination, info);
                           };
            }
        }
        private static TernaryAction<Cell, Cell, PasteInfo> CopyValueAndFormula
        {
            get
            {
                return delegate(Cell source, Cell destination, PasteInfo info)
                           {
                               if (source.Formula != null)
                               {
                                   destination.Value = source.Value;
                                   var p = new ExpressionParser(destination.Worksheet, null, LocalizationMode.NonLocalized, CellReferenceMode.Dynamic);
                                   Expression exp = p.Expression(source.Formula);
                                   if (p.Error != null)
                                       destination.Value = p.Error; //\\ forceformula
                                   else
                                   {
                                       try
                                       {
                                           if (!info.Cut)
                                               exp = exp.Offset(destination.Address - source.Address);
                                           if (info.Transpose)
                                               exp = exp.Transposed(destination);
                                       }
                                       catch (DimensionOutOfRangeException)
                                       {
                                           exp = new ErrorExpression(Errors.RefError);
                                       }
                                       destination.SetExpression(exp);
                                   }
                               }
                               else
                               {
                                   destination.Expression = null;
                                   destination.Value = source.Value;
                               }
                           };
            }
        }

        public static RangeFunction ApplyValidation
        {
            get
            {
                return new RangeFunction(null, null, null, null, null, TableFunctions.None, CopyDataValidation); //\\todo
            }
        }

        private static TernaryAction<Cell, Cell, PasteInfo> CopyValue
        {
            get { return CopyValueFn; }
        }

        public static RangeFunction ApplyFormula
        {
            get
            {
                return new RangeFunction(
                    delegate(Cell cell) { cell.Formula = null; },
                    null,
                    CopyValueAndFormula ,//\\ delegate(Cell source, Cell destination, PasteInfo info2) { destination.Formula = source.Formula; },
                    null, null, TableFunctions.None, CopyNoRange);
            }
        }

        private static void CopyValueFn(Cell source, Cell dest, PasteInfo pasteInfo)
        {
            dest.Value = source.Value;
        }

    }
}